#include "duktape.h"
#include "utils.h"
#include "ringbuffer.h"
#include "log.h"
//[]
static void put_length(duk_context* ctx,int len){
    duk_push_this(ctx);//[this]
    duk_push_int(ctx,len);//[this,len]
    duk_put_prop_string(ctx,-2,"length");//[this]
    duk_pop(ctx);//[]
}
static RingBuffer* get_buffer(duk_context*ctx);
//[]
static void save_buffer(duk_context* ctx,RingBuffer* buffer){
    duk_push_this(ctx);//[this]
    duk_push_pointer(ctx,buffer);//[this,buffer]
    duk_put_prop_string(ctx,-2,DUK_HIDDEN_SYMBOL("buffer"));//[this]
    duk_pop(ctx);//[]
}
//[]
static RingBuffer* get_buffer(duk_context*ctx){
    duk_push_this(ctx);//[this]
    duk_get_prop_string(ctx,-1,DUK_HIDDEN_SYMBOL("buffer"));//[this,buffer]
    RingBuffer* buffer = (RingBuffer*)duk_get_pointer(ctx,-1);
    duk_pop_2(ctx);//[]
    return buffer;
}
//[this]
static int final(duk_context* ctx){
    RingBuffer* buffer = get_buffer(ctx);
    if(buffer){
        ring_buffer_free(buffer);
    }
    return 0;
}

//[size,mode]
static int constructor(duk_context* ctx){
    int size = duk_get_int_default(ctx,0,0);
    if(size <= 0){
        duk_error(ctx,DUK_ERR_RANGE_ERROR,"ringbuffer size <= 0");
    }
    int mode = duk_get_int_default(ctx,1,REPLACE);
    RingBuffer* buffer = new_ring_buffer(size,mode);
    if(!buffer){        
        duk_error(ctx,DUK_RET_REFERENCE_ERROR,"ringbuffer malloc buffer failed");
    }
    duk_push_this(ctx);//[size,mode,this]
    save_buffer(ctx,buffer);
    put_length(ctx,0);
    put_number_field(ctx,2,"size",size);

    duk_push_c_function(ctx,final,1);
    duk_set_finalizer(ctx,-2);
    return 1;
}
//[buf]
static int write(duk_context* ctx){
    if(!duk_is_buffer_data(ctx,0)){
        duk_error(ctx,DUK_ERR_SYNTAX_ERROR,"ringbufer write arg0 should be Buffer like");
    }
    int bufsize = 0;
    char* inbuf = (char*)duk_get_buffer_data(ctx,0,&bufsize);
    if(bufsize <= 0){
        duk_error(ctx,DUK_ERR_RANGE_ERROR,"ringbuffer write in buffer size <= 0");
    }
    RingBuffer* buffer = get_buffer(ctx);
    int writed = ring_buffer_write(buffer,inbuf,bufsize);
    put_length(ctx,ring_buffer_len(buffer));
    duk_push_int(ctx,writed);
    return 1;
}
//[outbuf]
static int read(duk_context* ctx){
    if(!duk_is_buffer_data(ctx,0)){
        duk_error(ctx,DUK_ERR_SYNTAX_ERROR,"ringbufer read arg0 should be Buffer like");
    }
    int bufsize = 0;
    char* outbuf = (char*)duk_get_buffer_data(ctx,0,&bufsize);
    if(bufsize <= 0){
        duk_error(ctx,DUK_ERR_RANGE_ERROR,"ringbuffer read in buffer size <= 0");
    }
    RingBuffer* buffer = get_buffer(ctx);
    int readed = ring_buffer_read(buffer,outbuf,bufsize);
    put_length(ctx,ring_buffer_len(buffer));
    duk_push_int(ctx,readed);
    return 1;
}
static int dump(duk_context* ctx){
    RingBuffer* buffer = get_buffer(ctx);
    if(buffer){
        ring_buffer_dump(buffer);
    }
    return 0;
}
static duk_function_list_entry funcs[] = {
    {"iot.RingBuffer",constructor,2},
    {"write",write,1},
    {"read",read,1},
    {"dump",dump,0},
    {0/*name*/,0/*c function*/,0 /*nargs*/}
};

int ringbuffer_init(duk_context* ctx){
    register_class(ctx,funcs);
    return 0;
}